﻿using System;
using System.Collections.Generic;
using System.Linq;
using NeuralStock.DomainService.Interfaces;
using NeuralStock.DataAccess;
using NeuralStock.Common;
using System.IO;
using NeuralStock.Tools;
using NeuralStock.Entities;

namespace NeuralStock.DomainService.Implementation
{
    public class GamblerService : BaseService, IGamblerService
    {
        private readonly ICompanyService companyService;
        private readonly IFinancialReportService financialReportService;
        private readonly ICompanyQuotationService companyQuotationService;
        private readonly IFinancialAnalysisService financialAnalysisService;
        private readonly ICapEbitService capEbitService;

        private const int maxReturnYearSpan = 2;

        public GamblerService(ICompanyService companyService, IFinancialReportService financialReportService, ICompanyQuotationService companyQuotationService, IFinancialAnalysisService financialAnalysisService, ICapEbitService capEbitService)
        {
            this.companyService = companyService;
            this.financialReportService = financialReportService;
            this.companyQuotationService = companyQuotationService;
            this.financialAnalysisService = financialAnalysisService;
            this.capEbitService = capEbitService;
        }

        public GamblerResultData Process()
        {
            GamblerResultData result = new GamblerResultData();
            result.Results = new List<GamblerCompanyResultData>();
            result.ProcessLog = new List<string>();

            var companies = companyService.GetAll();

            foreach (var company in companies)
            {
                for (int year = 2005; year <= 2012; year++)
                {
                    try
                    {
                        var singleResult = ProcessForSingleCompany(company.Id, year);
                        if (singleResult.ResultForCapEbitdaByMinPrice.HasValue)
                        {
                            result.Results.Add(singleResult);
                        }
                    }
                    catch (Exception ex)
                    {
                        result.ProcessLog.Add(string.Format("Company: {0} Error: {1}", company.Name, ex.Message));
                    }
                }
            }

            result.Results = result.Results.OrderBy(x => x.Sector).ThenBy(x => x.CompanyName).ThenBy(x => x.LastReportYear).ToList();

            CreateGamblerSummaryFiles(result);

            return result;
        }

        // With parameters beacause it can be called separately, without processing for all companies
        public GamblerCompanyResultData ProcessForSingleCompany(int companyId, int lastReportYear)
        {
            GamblerCompanyResultData result = new GamblerCompanyResultData();

            var company = companyService.Get(companyId);

            result.CompanyName = company.Name;
            result.Sector = company.Sector;
            result.LastReportYear = lastReportYear;
            result.Id = result.CompanyName + result.LastReportYear;

            bool isHistorical = CaluclateWhetherIsHistorical(lastReportYear);

            int firstYearOfInvestment = lastReportYear + 1;

            FillCapEbitResults(companyId, lastReportYear, result, isHistorical, firstYearOfInvestment);

            FillFinancialAnalysisResults(companyId, lastReportYear, result);

            if (isHistorical)
            {
                CalculateHistoricalInvestmentResults(companyId, result, firstYearOfInvestment);
            }

            return result;
        }

        private static bool CaluclateWhetherIsHistorical(int lastReportYear)
        {
            bool isHistorical = false;
            if (lastReportYear != (DateTime.Today.Year - 1))
            {
                isHistorical = true;
            }
            return isHistorical;
        }

        private void FillCapEbitResults(int companyId, int lastReportYear, GamblerCompanyResultData result, bool isHistorical, int firstYearOfInvestment)
        {
            var capEbitResult = capEbitService.CalculateCapEbit(companyId, lastReportYear, firstYearOfInvestment, isHistorical);
            result.AvgCapEbitdaMinPrice = capEbitResult.AvgCapEbitdaMinPrice;
            result.CurrentCapEbitda = capEbitResult.CurrentCapEbitda;
            result.YearsAnalyzed = capEbitResult.YearsAnalyzed;
            result.PotentialGrowthByMinPrice = capEbitResult.PotentialGrowthByMinPrice;
            result.PotentialGrowthByMinPriceSafe = capEbitResult.PotentialGrowthByMinPriceSafe;
        }

        private void FillFinancialAnalysisResults(int companyId, int lastReportYear, GamblerCompanyResultData result)
        {
            var financialAnalysisResult = financialAnalysisService.PrepareFull(companyId, lastReportYear);
            result.EbitdaReturnOnEquityCapital_Vs3YearAvg = financialAnalysisResult.EbitdaReturnOnEquityCapital_Vs3YearAvg;
            result.ParticipationOfEquityCapitalInAssets_Vs3YearAvg = financialAnalysisResult.ParticipationOfEquityCapitalInAssets_Vs3YearAvg;
            result.FinancialAnalysisResult = financialAnalysisResult.AnalysisResult;
        }

        private void CalculateHistoricalInvestmentResults(int companyId, GamblerCompanyResultData result, int firstYearOfInvestment)
        {
            result.AchievedMaxReturn = companyQuotationService.GetMaxReturnForCompanyStartingAfterFirstQuarter(companyId, firstYearOfInvestment, firstYearOfInvestment + maxReturnYearSpan);
  
            if (ShouldInvest(companyId, result))
            {
                if (result.AchievedMaxReturn > result.PotentialGrowthByMinPrice)
                {
                    result.ResultForCapEbitdaByMinPrice = true;
                }
                else
                {
                    result.ResultForCapEbitdaByMinPrice = false;
                }
            }

            if (ShouldInvest(companyId, result, true))
            {
                if (result.AchievedMaxReturn > result.PotentialGrowthByMinPriceSafe)
                {
                    result.ResultForCapEbitdaByMinPriceSafe = true;
                }
                else
                {
                    result.ResultForCapEbitdaByMinPriceSafe = false;
                }
            }
        }

        private bool ShouldInvest(int companyId, GamblerCompanyResultData result, bool safe = false)
        {
            bool shouldInvest = false;

            if (!safe
                && result.PotentialGrowthByMinPrice > 0.5m)
                //&& result.PotentialGrowthByMinPrice < 8.0m)
            {
                shouldInvest = true;
            }
            else if (safe
                && result.PotentialGrowthByMinPriceSafe > 0.25m)
                //&& result.PotentialGrowthByMinPrice < 4.0m)
            {
                shouldInvest = true;
            }

            if (!result.FinancialAnalysisResult)
            {
                shouldInvest = false;
            }

            return shouldInvest;
        }

        private void CreateGamblerSummaryFiles(GamblerResultData result)
        {
            var csvResults = CsvExporter.ToCsv<GamblerCompanyResultData>(";", result.Results);
            string resultFileName = string.Format("Gambler{0}.csv", DateTime.Today.ToShortDateString());
            string resultFilePath = Path.Combine(DirectoryRepository.GamblerOutputDirectory, resultFileName);
            FileSaver.SaveFile(resultFilePath, csvResults);

            string processLog = string.Join("\n", result.ProcessLog);
            string processLogFileName = string.Format("Gambler{0}_ProcessLog.csv", DateTime.Today.ToShortDateString());
            string processLogFilePath = Path.Combine(DirectoryRepository.GamblerOutputDirectory, processLogFileName);
            FileSaver.SaveFile(processLogFilePath, processLog);
        }
    }
}
